package com.cm.projectx.service.analysis.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.cm.projectx.common.cache.SystemOrgCache;
import com.cm.projectx.entity.PriceTask;
import com.cm.projectx.entity.ProductPriceReport;
import com.cm.projectx.entity.ProductPriceReportDetail;
import com.cm.projectx.entity.TaskProcessRelation;
import com.cm.projectx.entity.analysis.ReqDto;
import com.cm.projectx.entity.analysis.RespDto;
import com.cm.projectx.entity.analysis.ValueMapPOJO;
import com.cm.projectx.mapper.PriceTaskMapper;
import com.cm.projectx.mapper.ProductPriceReportDetailMapper;
import com.cm.projectx.mapper.ProductPriceReportMapper;
import com.cm.projectx.mapper.TaskProcessRelationMapper;
import com.cm.projectx.response.Result;
import com.cm.projectx.service.analysis.IEggMilkSaltPriceAnalysisService;
import com.cm.projectx.util.PoiHelper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class EggMilkSaltPriceAnalysisServiceImpl implements IEggMilkSaltPriceAnalysisService {

    @Resource
    private PriceTaskMapper priceTaskMapper;

    @Resource
    private TaskProcessRelationMapper taskProcessRelationMapper;

    @Resource
    private ProductPriceReportMapper productPriceReportMapper;

    @Resource
    private ProductPriceReportDetailMapper productPriceReportDetailMapper;

    private static final DecimalFormat df = new DecimalFormat("0.00");

    @Data
    static class CacheData{
        private List<TaskProcessRelation> taskProcessRelations;
        private List<ProductPriceReport> productPriceReports;
        private List<ProductPriceReportDetail> productPriceReportDetails;
    }

    private CacheData getData(int taskId, String reportDate, String productName){
        CacheData cacheData = new CacheData();

        LambdaQueryWrapper<TaskProcessRelation> taskProcessRelationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        taskProcessRelationLambdaQueryWrapper.eq(TaskProcessRelation::getTaskId, taskId);
        taskProcessRelationLambdaQueryWrapper.eq(TaskProcessRelation::getStartTime, reportDate);
        List<TaskProcessRelation> taskProcessRelations = taskProcessRelationMapper.selectList(taskProcessRelationLambdaQueryWrapper);
        cacheData.setTaskProcessRelations(taskProcessRelations);
        if(CollectionUtils.isEmpty(taskProcessRelations)){
            return cacheData;
        }

        List<String> processIds = taskProcessRelations.stream().map(TaskProcessRelation::getProcessId).collect(Collectors.toList());
        LambdaQueryWrapper<ProductPriceReport> productPriceReportLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productPriceReportLambdaQueryWrapper.in(ProductPriceReport::getProcessId, processIds);
        List<ProductPriceReport> productPriceReports = productPriceReportMapper.selectList(productPriceReportLambdaQueryWrapper);
        cacheData.setProductPriceReports(productPriceReports);
        if(CollectionUtils.isEmpty(productPriceReports)){
            return cacheData;
        }

        List<String> parentIds = productPriceReports.stream().map(ProductPriceReport::getId).collect(Collectors.toList());
        LambdaQueryWrapper<ProductPriceReportDetail> productPriceReportDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productPriceReportDetailLambdaQueryWrapper.in(ProductPriceReportDetail::getParentId, parentIds);
        //productPriceReportDetailLambdaQueryWrapper.eq(ProductPriceReportDetail::getMetricType, "零售价格");
        productPriceReportDetailLambdaQueryWrapper.like(StringUtils.hasText(productName), ProductPriceReportDetail::getName, productName);
        List<ProductPriceReportDetail> productPriceReportDetails = productPriceReportDetailMapper.selectList(productPriceReportDetailLambdaQueryWrapper);
        cacheData.setProductPriceReportDetails(productPriceReportDetails);
        return cacheData;
    }

    private List<RespDto> generateData(CacheData nowDate, CacheData lastDate){
        //根据当前数据生成商品集合
        Map<String, RespDto> nowMap = new HashMap<>();
        //缓存当期商品价格
        Map<String, List<Double>> nowPriceMap = new HashMap<>();
        Map<String, ProductPriceReport> productPriceReportMap = nowDate.getProductPriceReports().stream().collect(Collectors.toMap(ProductPriceReport::getId, Function.identity()));
        List<ProductPriceReportDetail> nowProductPriceReportDetails = nowDate.getProductPriceReportDetails();
        for(ProductPriceReportDetail detail : nowProductPriceReportDetails){
            String productName = detail.getName();
            RespDto respDto = nowMap.get(productName);
            if(null == respDto){
                respDto = new RespDto();
                respDto.setProductName(productName);
                respDto.setProductLevel(detail.getKind());
                respDto.setProductUnit(detail.getUnit());
                respDto.setPriceType(detail.getMetricType());
                respDto.setFloatValue(detail.getFloatValue());
                respDto.setCircleValues(new ArrayList<>());
                respDto.setLastMetrics(new ArrayList<>());
                respDto.setNowMetrics(new ArrayList<>());
                respDto.setMonitorPrices(new ArrayList<>());
                nowMap.put(productName, respDto);
            }

            //记录监测点
            ProductPriceReport productPriceReport = productPriceReportMap.get(detail.getParentId());
            if(null != productPriceReport){
                ValueMapPOJO valueMapPOJO = new ValueMapPOJO();
                valueMapPOJO.setName(productPriceReport.getMonitorPoint());
                valueMapPOJO.setValue(detail.getPrice()+"");
                respDto.getMonitorPrices().add(valueMapPOJO);
            }

            //缓存价格
            List<Double> priceList = nowPriceMap.computeIfAbsent(productName, key ->new ArrayList<>());
            priceList.add(detail.getPrice());
        }

        //缓存上期商品价格
        Map<String, List<Double>> lastPriceMap = new HashMap<>();
        List<ProductPriceReportDetail> lastProductPriceReportDetails = lastDate.getProductPriceReportDetails();
        for(ProductPriceReportDetail detail : lastProductPriceReportDetails){
            RespDto respDto = nowMap.get(detail.getName());
            if(null != respDto){
                //缓存价格
                List<Double> priceList = lastPriceMap.computeIfAbsent(detail.getName(), key ->new ArrayList<>());
                priceList.add(detail.getPrice());
            }
        }
        List<RespDto> list = new ArrayList<>();
        //计算数值
        for(Map.Entry<String, RespDto> entry : nowMap.entrySet()){
            String productName = entry.getKey();
            RespDto respDto = entry.getValue();
            //计算当期平均值
            ValueMapPOJO nowAvgPOJO = new ValueMapPOJO();
            double nowAvg = 0;
            nowAvgPOJO.setName("平均价");
            List<Double> nowPriceList = nowPriceMap.get(productName);
            if(CollectionUtils.isNotEmpty(nowPriceList)){
                nowAvg = nowPriceList.stream().filter(Objects::nonNull).mapToDouble(Double::doubleValue).average().orElse(0);
            }
            nowAvgPOJO.setValue(df.format(nowAvg));
            respDto.getNowMetrics().add(nowAvgPOJO);

            //计算上期平均值
            ValueMapPOJO lastAvgPOJO = new ValueMapPOJO();
            double lastAvg = 0;
            lastAvgPOJO.setName("平均价");
            List<Double> lastPriceList = lastPriceMap.get(productName);
            if(CollectionUtils.isNotEmpty(lastPriceList)){
                lastAvg = lastPriceList.stream().filter(Objects::nonNull).mapToDouble(Double::doubleValue).average().orElse(0);
            }
            lastAvgPOJO.setValue(df.format(lastAvg));
            respDto.getLastMetrics().add(lastAvgPOJO);

            //计算环比
            ValueMapPOJO radioPOJO = new ValueMapPOJO();
            radioPOJO.setName("环比");
            double ratio;
            if(nowAvg == 0){
                ratio = 0;
            }else if(lastAvg == 0){
                ratio = 100;
            }else {
                ratio = (nowAvg - lastAvg) / lastAvg * 100;
            }
            radioPOJO.setValue(df.format(ratio));
            respDto.getCircleValues().add(radioPOJO);

            list.add(respDto);
        }

        return list;
    }

    /**
     * 输出数据分析
     * @param dto 查询参数
     * @return 数据分析
     */
    @Override
    public Result getPriceAnalysis(ReqDto dto) {
        Result result = new Result();

        int taskId = dto.getTaskId();
        PriceTask priceTask = priceTaskMapper.selectById(taskId);
        if(null == priceTask){
            result.setCode(-1);
            result.setMsg("任务不存在。");
            return result;
        }

        String nowReportDate = dto.getNowReportDate();
        if(!StringUtils.hasText(nowReportDate)){
            result.setCode(-1);
            result.setMsg("当前时间不能为空。");
            return result;
        }
        String lastReportDate = StringUtils.hasText(dto.getLastReportDate())? dto.getLastReportDate(): nowReportDate;
        CacheData nowData = getData(taskId, nowReportDate, dto.getProductName());
        if(CollectionUtils.isEmpty(nowData.getProductPriceReportDetails())){
            result.setCode(200);
            result.addData("priceResult", new ArrayList<>());

        }else {
            CacheData lastData = getData(taskId, lastReportDate, dto.getProductName());
            result.setCode(200);
            result.addData("priceResult", generateData(nowData, lastData));
        }

        return result;
    }

    @Override
    public void exportPriceAnalysis(ReqDto dto, HttpServletResponse response, String type) {
        String name = "价格监测报表";
        switch (type){
            case "egg": name = "鸡蛋价格监测报表"; break;
            case "milk": name = "牛奶及相关商品价格监测报表"; break;
            case "salt": name = "食盐价格监测报表"; break;
            case "agriProduct": name = "农业生产资料加个管理"; break;
            case "wheat": name = "小麦、面粉价格检测报表"; break;
            case "corn": name = "玉米及相关商品价格监测报表"; break;
            case "cookingOilPrice": name = "食用油价格监测报表"; break;
            case "pork": name = "生猪及相关商品价格监测报表"; break;
            default:;
        }
        int taskId = dto.getTaskId();
        PriceTask priceTask = priceTaskMapper.selectById(taskId);
        if(null == priceTask){
            log.error("任务不存在。");
            return;
        }
        String nowReportDate = dto.getNowReportDate();
        if(!StringUtils.hasText(nowReportDate)){
            log.error("当前时间不能为空。");
            return;
        }
        String lastReportDate = StringUtils.hasText(dto.getNowReportDate())? dto.getNowReportDate(): nowReportDate;
        List<RespDto> list = null;
        CacheData nowData = getData(taskId, nowReportDate, dto.getProductName());
        if(!CollectionUtils.isEmpty(nowData.getProductPriceReportDetails())){
            CacheData lastData = getData(taskId, lastReportDate, dto.getProductName());
            list = generateData(nowData, lastData);
        }
        Map<String, String> monitorMap = getMonitorMap(nowData);
        XSSFWorkbook workbook = generateExportFile(list, name, monitorMap);
        Map<String, Object> fileInfo = new HashMap<>();
        fileInfo.put("excel_file_name", name + ".xlsx");
        fileInfo.put("excel_file_content", workbook);
        PoiHelper.downFileByBrowser(fileInfo, response);
    }

        private static Map<String, String> getMonitorMap(CacheData nowData){
        List<ProductPriceReportDetail> productPriceReportDetails = nowData.getProductPriceReportDetails();
        List<ProductPriceReport> productPriceReports = nowData.getProductPriceReports();

        Map<String, String> monitorMap = new HashMap<>();
            if(CollectionUtils.isEmpty(productPriceReportDetails)){
                return monitorMap;
            }
        if(CollectionUtils.isEmpty(productPriceReports)){
            return monitorMap;
        }
        Set<String> monitorIds = new HashSet<>();
        Map<String, ProductPriceReport> productPriceReportIdMap = productPriceReports.stream().collect(Collectors.toMap(ProductPriceReport::getId, Function.identity()));
        for(ProductPriceReportDetail detail : productPriceReportDetails){
            String parentId = detail.getParentId();
            ProductPriceReport report = productPriceReportIdMap.get(parentId);
            if(report == null){
                continue;
            }
            String monitorId = report.getMonitorPoint();
            monitorIds.add(monitorId);
        }
        for(String monitorId : monitorIds){
            monitorMap.put(monitorId, SystemOrgCache.getOrgName(monitorId));
        }
        return monitorMap;
    }

//    private static Map<String, String> getMonitorMap(String productMonitorRelations){
//        Map<String, String> monitorMap = new HashMap<>();
//        if(!StringUtils.hasText(productMonitorRelations)){
//            return monitorMap;
//        }
//        JSONArray jsonArray = JSON.parseArray(productMonitorRelations);
//        if(null == jsonArray){
//            return monitorMap;
//        }
//        for(int i=0; i<jsonArray.size(); i++){
//            JSONObject jsonObject = jsonArray.getJSONObject(i);
//            JSONArray monitors = jsonObject.getJSONArray("monitors");
//            if(null == monitors){
//                continue;
//            }
//            for(int j=0; j<monitors.size(); j++){
//                JSONObject monitor = monitors.getJSONObject(j);
//
//                monitorMap.put(monitor.getString("monitorId"), monitor.getString("monitorName"));
//            }
//        }
//        return monitorMap;
//    }

    private XSSFWorkbook generateExportFile(List<RespDto> list, String name, Map<String, String> monitorMap){
        XSSFWorkbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(name);
        Map<String, Integer> monitorSotrMap = generateExportFileHeader(workbook, monitorMap);
        //填充数据行
        if(CollectionUtils.isEmpty(list)){
            return workbook;
        }
        int rowNum = 1;
        for (RespDto dto : list) {
            Row row = sheet.createRow(rowNum++);
            int cellNum = 0;
            row.createCell(cellNum++).setCellValue(dto.getProductName() != null ? dto.getProductName() : "");
            row.createCell(cellNum++).setCellValue(dto.getProductLevel() != null ? dto.getProductLevel() : "");
            row.createCell(cellNum++).setCellValue(dto.getProductUnit() != null ? dto.getProductUnit() : "");
            String radio = dto.getCircleValues().get(0).getValue();
            row.createCell(cellNum++).setCellValue(radio != null ? radio : "");
            String lastPrice = dto.getLastMetrics().get(0).getValue();
            row.createCell(cellNum++).setCellValue(lastPrice != null ? lastPrice : "");
            String nowPrice = dto.getNowMetrics().get(0).getValue();
            row.createCell(cellNum++).setCellValue(nowPrice != null ? nowPrice : "");
            row.createCell(cellNum++).setCellValue(dto.getPriceType() != null ? dto.getPriceType() : "");
            List<ValueMapPOJO> monitorPrices = dto.getMonitorPrices();
            for(Map.Entry<String, Integer> entry : monitorSotrMap.entrySet()){
                Integer index = entry.getValue();
                String value = "0.0";
                row.createCell(index).setCellValue(value);
            }
            for(ValueMapPOJO monitorPrice : monitorPrices){
                String monitorId = monitorPrice.getName();
                String value = monitorPrice.getValue();
                int index = monitorSotrMap.getOrDefault(monitorId, -1);
                if(index > -1){
                    Cell cell = row.getCell(index);
                    if(cell != null){
                        cell.setCellValue(value);
                    } else {
                        row.createCell(index).setCellValue(value);
                    }

                }
            }
        }
        return workbook;
    }

    /**
     * 生成表头
     * @param workbook xlsx对象
     * @param monitorMap 监测点集合 id->name
     * @return 监测点位置集合 id->sort
     */
    private Map<String, Integer> generateExportFileHeader(XSSFWorkbook workbook , Map<String, String> monitorMap){
        // 3. 创建表头样式
        CellStyle headerStyle = workbook.createCellStyle();
        Font headerFont = workbook.createFont();
        headerFont.setBold(true); // 加粗
        headerStyle.setFont(headerFont);
        headerStyle.setAlignment(HorizontalAlignment.CENTER); // 居中
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 添加边框
        headerStyle.setBorderTop(BorderStyle.THIN);
        headerStyle.setBorderBottom(BorderStyle.THIN);
        headerStyle.setBorderLeft(BorderStyle.THIN);
        headerStyle.setBorderRight(BorderStyle.THIN);

        List<String> headerList = new ArrayList<>();
        headerList.add("商品名称");
        headerList.add("规格等级");
        headerList.add("计量单位");
        headerList.add("环比");
        headerList.add("上期平均价");
        headerList.add("平均价");
        headerList.add("价格类型");
        //根据最大监测点数计算列数
//        int monitorNum = 0;
//        if(CollectionUtils.isNotEmpty(list)){
//            for(RespDto dto : list){
//                if(CollectionUtils.isNotEmpty(dto.getMonitorPrices())){
//                    if(monitorNum < dto.getMonitorPrices().size()){
//                        monitorNum = dto.getMonitorPrices().size();
//                    }
//                }
//            }
//        }
//        if(monitorNum > 0){
//            char c = 'A';
//            String prefix = "监测点";
//            for(int index = 0; index < monitorNum; index++){
//                headerList.add(prefix + c++);
//            }
//        }
        Map<String,Integer> monitorSortMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(monitorMap)){
            for(Map.Entry<String, String> entry : monitorMap.entrySet()){
                monitorSortMap.put(entry.getKey(), headerList.size());
                headerList.add(entry.getValue());
            }
        }
        Sheet sheet = workbook.getSheetAt(0);
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < headerList.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headerList.get(i));
            cell.setCellStyle(headerStyle);
            sheet.setColumnWidth(i, 20*256);
        }
        return monitorSortMap;
    }
}
